/*实现异步日志缓冲区*/
#ifndef __BUFFER_HPP__
#define __BUFFER_HPP__


#include<vector>
#include"util.hpp"
#include<cassert>
namespace kaizi
{
    #define DEFAULT_BUFFER_SIZE (1024*1024*10) // 默认缓冲区大小为10M
    #define THRESHOLD_BUFFER_SIZE (1024*1024*80)//阈值
    #define INCREMENT_BUFFER_SIZE (1024*1024*10)//增值
    class Buffer
    {
    public:
        Buffer()
        :_buffer(DEFAULT_BUFFER_SIZE),_reader_idx(0),_writer_idx(0)//默认大小和两个读写指针
        {}
        //向缓冲区中写入数据
        void push(const char*data,size_t len)
        {
            //缓冲区剩余空间不够的情况，1、扩容  2、阻塞/返回false
            
            //先检测空间大小

            //1、固定大小，则直接返回
            //if(len>writeAbleSize())return ;
            //2、动态空间，用于极限性能测试---扩容
            ensureEnoughSize(len);//进行内存扩充操作，先进行判断，再决定扩容的方式

            //1、将数据拷贝到缓冲区
            std::copy(data,data+len,&_buffer[_writer_idx]);//将从data开始长度为len个字节的数据拷贝到_buffer缓冲区此时的_write_idx位置
            //2、将当前写入位置向后偏移
            moveWriter(len);//写入位置向后进行偏移len位
        }

        size_t writeAbleSize()
        {
            //对于扩容思路，不存在可写空间大小，因为总是可写
            
            //因此这个接口仅仅针对固定大小缓冲区提供
            return _buffer.size()-_writer_idx;//总大小-可写入的起始地址=剩余可写地址
        }

        //获取可读数据起始地址
        const char* begin()
        {
            return &_buffer[_reader_idx];//将读地址传过去就行了
        }
        //返回可读数据长度
        size_t readAbleSize()
        {
            //两个指针中间的就是我们实际的数据长度了，因为当前缓冲区并不是循环缓冲区，因此只会向后写
           //当前缓冲区的思想是双缓冲区的思想
            return _writer_idx-_reader_idx;//可读数据长度=可写数据长度-读数据长度
        } 
        //对读写指针向后偏移操作
        void moveReader(size_t len)//可读空间大小
        {
            assert(len<=readAbleSize());//偏移的长度len必须小于可读数据的长度
            _reader_idx+=len;//进行偏移擦操作
        }
        //重置读写位置，初始化缓冲区
        void reset()
        {
            //将两个指针的位置都重置为0，表示此时缓冲区内的空间都是空闲空间
            //两个指针相等，表示没有数据可读 
            _reader_idx=0;
            _writer_idx=0;
        }
        //对buffer进行交换操作，两个缓存区进行交换
        void swap( Buffer&buffer)
        {
            //将两个buffer的读写地址进行交换，将缓冲区里面的空间地址进行交换
            _buffer.swap(buffer._buffer);
            std::swap(_reader_idx,buffer._reader_idx);
            std::swap(_writer_idx,buffer._writer_idx);
        }
        //判断缓冲区是否为空
        bool empty()
        {
            return (_reader_idx==_writer_idx);//两个指针相等，表示没有数据可读 
        }
    private:
        //对空间进行扩容操作
        void ensureEnoughSize(size_t len)//内存下小于80MB的进行线性增长，一次+10，如果大于的话就进行翻倍增长
        {
            if(len<=writeAbleSize())return ;//不需要扩容的情况
            size_t new_size=0;
            if(_buffer.size()<THRESHOLD_BUFFER_SIZE)//小于阈值的话则进行翻倍增长操作
            {
                //小于阈值的话则进行翻倍增长操作
                new_size=_buffer.size()*2+len;
            }
            else//大于等于阈值大小的话
            {
                new_size=_buffer.size()+INCREMENT_BUFFER_SIZE+len;//线性增长，一次增加10MB
            }
            _buffer.resize(new_size);//对缓冲区进行调整大小，调整为新的大小
        } 

        //对读写指针向后偏移操作
        void moveWriter(size_t len)
        {
            assert(len+_writer_idx<=_buffer.size());//偏移的长度len必须整个缓冲区的长度
            _writer_idx+=len;//进行偏移擦操作
        }

    private:
    
        std::vector<char>_buffer;//缓冲区使用vector进行管理 
        size_t _reader_idx;//当前可读数据的指针，本质是下标
        size_t _writer_idx;//当前可写数据的指针，本质是下标
    };
}
#endif